package Model;

import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import Model.Conference.Role;

/**
 * A Conference with a set/list of users that can be mapped to 
 * an Enum Role.
 * 
 * @author InnoIrving V. Estrera
 * */
public class Conference
{
  public static final int LIMIT = 4;
  public static final int UNDECIDED = 0;
  public static final int ACCEPTED = 1;
  public static final int REJECTED = -1;

  private String my_conference_name;
  
  /*private invariant getRoles(programChair()).contains(Role.PROGRAM_CHAIR)
                      && getRoles(programChair()).contains(Role.SUB_CHAIR)
                      && programChair() != null;*/
  private User my_program_chair;

  /*private invariant (\forall int i; i >= 0;
                        userMappings().contains(getSubmittedPapers().get(i)));*/
  private List<Paper> my_submitted_papers; // all submitted papers
  
  /*private invariant (\forall int i; i >= 0;
                        userMappings().contains(subProgramChairs().get(i)));*/
  private List<User> my_sub_chairs = new ArrayList<User>();
  
  //private invariant (\forall User a_user; userMappings().get(a_user) != null);
  private Map<User, List<Role>> my_users;
  
  
  // @ private invariant getDeadline() >= current_date;
  private Date my_submission_deadline;

  /**
   * Serves to prevent instantiation of Conference class with no arguments
   * */
  private Conference()
  {
    // prevents instantiation of Conference() with no arguments
  }

  /**
   * Create a conference with no registered users other than the program chair.
   * Deadlines for this conference are not yet set, and are initialized to 0.
   * 
   * @param the_conference_name
   *          The name of this conference.
   * @param the_program_chair
   *          The program chair as a User object.
   * */
  // @ ensures containsSubchair(my_program_chair);
  // @ ensures containsUser(my_program_chair);
  // @ ensures my_conference_name.equals(the_conference_name);
  public Conference(final String the_conference_name,
      final/* @ non_null @ */User the_program_chair, final Date the_deadline)
  {
    this(the_conference_name, the_program_chair, new ArrayList<User>(),
        the_deadline);
  }
  
  /**
   * Creates a conference with specified users with unassigned roles. Deadlines
   * for this conference are not yet set, and are initialized to 0.
   * 
   * Edit by Paul: Fixed Logical error causing PC to have role overwritten.
   * 
   * @author Paul Schultz (ps87@uw.edu)
   * @author Irving Estrera
   * @param the_conference_name
   *          The name of this conference.
   * @param the_program_chair
   *          The program chair as a User object.
   * @param the_users
   *          The list of User objects that will be populated to this
   *          conference.
   * */
  // @ ensures containsSubchair(my_program_chair);
  // @ ensures containsUser(my_program_chair);
  // @ ensures my_conference_name.equals(the_conference_name);
  // @ ensures users().containsAll(the_users);
  public Conference(final String the_conference_name,
      final/* @ non_null @ */User the_program_chair,
      final/* @ non_null @ */List<User> the_users, final Date the_deadline)
  {

    my_conference_name = the_conference_name;
    my_program_chair = the_program_chair; // BSR3, BSR4
    my_submission_deadline = the_deadline;
    my_submitted_papers = new ArrayList<Paper>();
    my_users = new HashMap<User, List<Role>>();
    my_sub_chairs = new ArrayList<User>(); // empty list
    boolean contains_pc = false;
    for (int index = 0; index < the_users.size(); index++)
    {
      if (the_users.get(index).equals(the_program_chair))
      {
        contains_pc = true;
        break;
      }
    }

    for (int index = 0; index < the_users.size(); index++)
    {
      my_users.put(the_users.get(index), new ArrayList<Role>());
    }
    
    if (contains_pc)
    {
      List<Role> pc_role = new ArrayList<Role>();
      pc_role.add(Role.PROGRAM_CHAIR);
//      pc_role.add(Role.SUB_CHAIR);
      my_users.put(the_program_chair, pc_role);
    }

//    my_sub_chairs.add(my_program_chair);

  }

  /**
   * Create a conference object with users and their specific roles. Assumes
   * that every key(User) in the Map<User, List<Role>> is contained in
   * List<User> the_users. Deadlines for this conference are not yet set, and
   * are initialized to 0.
   * 
   * @param the_conference_name
   *          The name of this conference.
   * @param the_program_chair
   *          The program chair as a User object.
   * @param the_users
   *          The list of User objects that will be populated to this
   *          conference.
   * @param the_roles
   *          The mapping of roles and users for this conference.
   * */
  // @ ensures containsSubchair(my_program_chair);
  // @ ensures containsUser(my_program_chair);
  // @ ensures my_conference_name.equals(the_conference_name);
  public Conference(final String the_conference_name,
      final/* @ non_null @ */User the_program_chair,
      final/* @ non_null @ */Map<User, List<Role>> the_roles,
      final Date the_deadline)
  {
    // Implements Business Rules # 1,2,3,4
    my_conference_name = the_conference_name;
    my_program_chair = the_program_chair;
    my_submission_deadline = the_deadline;
    my_submitted_papers = new ArrayList<Paper>();
    my_users = new HashMap<User, List<Role>>();

    my_users.putAll(the_roles);
    if (!containsUser(my_program_chair))
    {
      List<Role> pc_role = new ArrayList<Role>();
      pc_role.add(Role.PROGRAM_CHAIR);
//      pc_role.add(Role.SUB_CHAIR);
//      pc_role.add(Role.REVIEWER);
      my_users.put(the_program_chair, pc_role);
    }

    // populate subprogram chair list
    // iterate through Map of users vs. roles and find sub program chairs
    Iterator<User> itr;
    for (itr = my_users.keySet().iterator(); itr.hasNext();)
    {
      User key = itr.next();
      if (my_users.get(key).contains(Role.SUB_CHAIR))
      {
        my_sub_chairs.add(key);
      }
    }
  }

  /**
   * Adds a paper to the conference. The user submitting a paper must be first
   * added to the conference, otherwise, this method will throw an exception. If
   * the same user has submitted more than the limit, this method will throw an
   * exception. This method assumes that the submission date has not yet passed.
   * 
   * @param the_submitted_paper
   *          The paper to be added.
   * */
  // @ requires containsUser(the_submitted_paper.getAuthor());
  // @ requires containsSubchair(the_submitted_paper.getSubprogramChair());
  // @ requires new Date().before(my_submission_deadline);
  // @ requires !hasReachedLimit(the_submitted_paper.getAuthor());
  // @ ensures \old(getSubmittedPapers().size()) < getSubmittedPapers().size();
  // @ ensures getSubmittedPapers().contains(the_submitted_paper);
  /*
   * @ ensures my_users.get(the_submitted_paper.getAuthor())
   * .contains(Role.AUTHOR);
   */
  public void addPaper(final Paper the_submitted_paper)
  {
    // Implements Business Rule #15
    // submission date check can be handled in GUI
    if (!containsUser(the_submitted_paper.getAuthor()))
    {
      throw new IllegalArgumentException(the_submitted_paper
          + " is not registered to the conference");
    }

    if (!containsSubchair(the_submitted_paper.getSubprogramChair())
        && (the_submitted_paper.getSubprogramChair() != null))
    {
      throw new IllegalArgumentException(the_submitted_paper
          + " has been assigned to a subprogram chair that is not registered"
          + " to " + conferenceName());
    }

    if (new Date().after(my_submission_deadline))
    {
      throw new IllegalArgumentException("Deadline has passed. Deadline was "
          + my_submission_deadline + ". Current date: " + new Date());
    }

    if (hasReachedLimit(the_submitted_paper.getAuthor()))
    {
      throw new IllegalArgumentException("Limit of papers reached");
    } else
    {
      my_submitted_papers.add(the_submitted_paper); // submit paper
      updateRole(the_submitted_paper.getAuthor(), Role.AUTHOR);
    }
  }

  /**
   * Removes a paper from the conference. If paper doesnt exist, this throws an
   * exception.
   * 
   * @param the_submitted_paper
   *          The paper to be removed from the conference.
   * */
  // @ requires getSubmittedPapers().contains(the_submitted_paper);
  // @ requires hasSubmittedAPaper(the_submitted_paper.getAuthor());
  // @ requires containsUser(the_submitted_paper.getAuthor());
  /*
   * @ ensures getSubmittedPapers().size() < \old(getSubmittedPapers().size());
   */

  // @ ensures !getSubmittedPapers().contains(the_submitted_paper);
  public void removePaper(final Paper the_submitted_paper)
  {
    if (my_submitted_papers.contains(the_submitted_paper))
    {
      my_submitted_papers.remove(the_submitted_paper);
    } else
    {
      throw new IllegalArgumentException(
          "Paper does not exist in this conference");
    }
  }

  /**
   * Returns the subprogram chair's recommendation only if it satisfies the
   * following requirements. The second parameter is either the subprogram chair
   * for that paper, or is the program chair for this conference.
   * 
   * @param the_paper
   *          The paper associated.
   * @param the_user
   *          The user trying to access this paper's recommendation.
   * @return A File object of the recommendation.
   * */
  // @ requires containsUser(the_user);
  /*
   * @ requires the_user.equals(my_program_chair) ||
   * the_user.equals(the_paper.getSubprogramChair())
   */
  // @ ensures \result == the_paper.getRecmmendation();
  // also assert that this conference was not changed, and is still the same
  // state
  public File getRecommendation(final Paper the_paper, final User the_user)
  {
    // Enforces Business Rule #14
    if (!containsUser(the_user))
    {
      throw new IllegalArgumentException(the_user
          + " is not registered to conference " + conferenceName());
    }
    if (the_user.equals(my_program_chair)
        || the_user.equals(the_paper.getSubprogramChair()))
    {
      return the_paper.getRecommendation();
    } else
    {
      throw new IllegalArgumentException(the_user + " is not allowed to access"
          + "this recommendation file");
    }
  }

  /**
   * Adds a user to the conference with no specified roles. This method does not
   * allow duplicate users to be added, otherwise it will throw an Exception.
   * 
   * @param the_user
   *          The user to be added.
   * @return Returns true if user was added. False otherwise.
   * */
  // @ requires !containsUser(the_user);
  // @ ensures containsUser(the_user);
  // @ ensures users().size() > \old(users().size());
  public void addUser(final User the_user)
  {
    // cannot add user if this conference already has that user
    if (containsUser(the_user))
    {
      throw new IllegalArgumentException(the_user + " already exists" + "in "
          + conferenceName() + ". Make sure email OR username"
          + " is not registered to this conference!");
    } else
    {
      my_users.put(the_user, new ArrayList<Role>());
    }
  }

  /**
   * Assigns a registered user to become a reviewer. If the given user is not
   * registered in the conference, this method will return an exception.
   * 
   * @return the_user The user to be assigned as a reviewer.
   * */
  // @ requires !containsUser(the_user);
  // @ ensures getRoles(the_user).contains(Role.REVIEWER);
  // @ ensures getRoles(the_user) > \old(getRoles(the_user));
  public void assignReviewer(final User the_user)
  {
    updateRole(the_user, Role.REVIEWER);
  }

  /**
   * Assigns a registered user to become a sub program chair. The registered
   * user must already been assigned the reviewer role. Otherwise, this method
   * will return an exception.
   * 
   * @param the_user
   *          The user to be assigned as a subprogram chair.
   * @return Returns true if the user was assigned as a subprogram chair. False
   *         otherwise.
   * */
  // @ requires containsUser(the_user);
  // @ requres getRoles(the_user).contains(Role.REVIEWER);
  // @ requires !getRoles(the_user).contains(Role.SUB_CHAIR);
  // @ ensures getRoles(the_user).contains(Role.REVIEWER);
  // @ ensures getRoles(the_user).contains(Role.SUB_CHAIR);
  /*
   * @ ensures getRoles(the_user).size() > \old(getRoles(the_user).size());
   */
  public void assignSubChair(final User the_user)
  {
    if (updateRole(the_user, Role.SUB_CHAIR))
    {
      my_sub_chairs.add(the_user);
    } else
    {
      throw new IllegalArgumentException(the_user
          + " is not eligible to become a subprogram chair. " + the_user
          + " must first be assigned the Reviewer role");
    }
  }

  /**
   * Assigns a subprogram chair to a paper. This method will throw an exception
   * given the following conditions, the current user has already been assigned
   * 4 different papers, the user is not assigned as a subprogram chair, the
   * paper being added is a duplicate of another paper.
   * 
   * @param the_paper
   *          The paper to be assigned.
   * @param the_user
   *          The subprogram chair.
   * */
  // @ requires containsUser(the_chair);
  // @ requires containsUser(the_paper.getAuthor());
  // @ requires !containsSubchair(the_paper.getSubprogramChair());
  // @ requires getPapersAssignedToSubChairs(the_chair).size() < LIMIT;
  // @ requires !getPapersAssignedToSubChairs(the_chair).contains(the_paper);
  // @ requires !the_chair.equals(the_paper.getAuthor());
  /*
    @ ensures getPapersAssignedToSubChairs(the_chair).size() >
              \old(getPapersAssignedToSubChairs(the_chair).size() < LIMIT);
   */
  // @ ensures getPapersAssignedToSubChairs(the_chair).contains(the_paper);
  public void assignPaperToSubChair(final Paper the_paper, final User the_chair)
  {
    if (!containsUser(the_paper.getAuthor()))
    {
      throw new IllegalArgumentException(the_paper.getAuthor()
          + " is not registered to the conference");
    }

    if (!containsUser(the_chair))
    {
      throw new IllegalArgumentException(the_chair
          + " is not registered to the conference");
    }

    if ((the_paper.getSubprogramChair() != null)
        && !containsSubchair(the_paper.getSubprogramChair()))
    {
      throw new IllegalArgumentException(the_paper
          + " has been assigned to a subprogram chair that is not registered"
          + " to " + conferenceName());
    }
    List<Paper> papers_assigned = getPapersAssignedToSubChairs(the_chair);
    boolean under_limit = papers_assigned.size() < LIMIT;
    boolean a_subchair = containsSubchair(the_chair);
    boolean a_duplicate = papers_assigned.contains(the_paper);
    boolean assigned_to_itself = the_paper.getAuthor().equals(the_chair);
    if (!a_duplicate && under_limit && a_subchair && !assigned_to_itself)
    {
      the_paper.setSubprogramChair(the_chair);
    } else
    {
      throw new IllegalArgumentException(the_paper.getTitle()
          + " has already been assigned to " + the_paper.getSubprogramChair()
          + " OR " + the_chair + " is NOT a sub program chair" +
          "OR " + the_chair + " is the author of this paper");
    }
  }

  /**
   * Assigns papers to reviewers. When this method is called, it initializes the
   * current review file to null in the associated Paper object. This method
   * throws an exception if the user passed in the second parameter is either
   * not registered to the conference or has reached the limit of assigned
   * reviews to him/her.
   * 
   * @param the_paper
   *          The paper to review and associated to the reviewer
   * @param the_user
   *          The reviewer
   * */
  // @ requires containsUser(the_user);
  // @ requires containsUser(the_paper.getAuthor());
  // @ requires getPapersAssignedToReviewers(the_user).size() < 4;
  // @ requires !the_user.equals(the_paper.getAuthor());
  // @ requires getRoles(the_user).contains(Role.REVIEWER);
  // @ requires !getPapersAssignedToReviewers(the_user).contains(the_paper);
  // @ ensures the_paper.getMyReviewAssociation().containsKey(the_user); 
  public void assignPaperToReviewers(final Paper the_paper, final User the_user)
  {
    // A Reviewer can be assigned to review a maximum of 4 papers to review.
    if (!containsUser(the_user))
    {
      throw new IllegalArgumentException(the_user + " is not registered to "
          + conferenceName());
    }
    if (!containsUser(the_paper.getAuthor()))
    {
      throw new IllegalArgumentException(the_paper.getAuthor()
          + " is not registered to " + conferenceName());
    }

    boolean is_a_reviewer = getRoles(the_user).contains(Role.REVIEWER);
    boolean under_limit = getPapersAssignedToReviewers(the_user).size() < 4;
    boolean is_duplicate = false;
    boolean assigned_to_itself = the_paper.getAuthor().equals(the_user);
    if (is_a_reviewer)
    {
      is_duplicate = getPapersAssignedToReviewers(the_user).contains(the_paper);
    }

    if (assigned_to_itself) {
      throw new IllegalArgumentException("You cannot assign " + the_paper + " to " + the_user +
          " because he is the author of this paper");
    }

    if (under_limit && !is_duplicate)
    {
      the_paper.getMyReviewAssociation().put(the_user, null);
    } else
    {
      throw new IllegalArgumentException("Either " + the_user
          + " is not assigned"
          + " the reviewer role or has already been assigned the paper "
          + the_paper);
    }
  }

  // helper methods

  /**
   * Assigns a specific role to a user to this conference, by first checking
   * their eligibility to become that role. This method will give an exception
   * if the user is not registered, or have not met the conditions of becoming
   * that assigned role.
   * 
   * @param the_user
   *          The user to be assigned a new role.
   * @param the_assigned_role
   *          The new role.
   * @return Returns true if it was able to assign the user, false otherwise.
   * */
  //@ requires containsUser(the_user);
  //@ requires checkEligibility(the_user, the_assigned_role);
  //@ ensures getRoles(the_user).contains(the_assigned_role);
  public boolean updateRole(final User the_user, final Role the_assigned_role)
  {
    if (!containsUser(the_user))
    {
      throw new IllegalArgumentException(the_user
          + " is not registered to this conference");
    }
    boolean result = false;
    // checks eligibility of the_user for the_assigned_role
    if (checkEligibility(the_user, the_assigned_role))
    {
      my_users.get(the_user).add(the_assigned_role);// adds role of user's list
                                                    // of roles
      result = true;
    }
    return result;
  }

  /**
   * Checks eligibility of a user to become a specific role. This method will
   * give an exception if the user is not registered, or have not met the
   * conditions of becoming that assigned role.
   * 
   * @param the_user
   *          The user to be assigned a new role.
   * @param the_assigned_role
   *          The new role.
   * @return Returns true if it the user is eligible, false otherwise.
   * */
  //@ requires containsUser(the_user);
  //@ ensures \result == the_eligibility
  public boolean checkEligibility(final User the_user,
      final Role the_assigned_role)
  {
    // throws an exception if the user is not registered in the conference

    boolean result = false;
    switch (the_assigned_role)
    {
    case AUTHOR: // Business Rule #6
      if (hasSubmittedAPaper(the_user)
          && !my_users.get(the_user).contains(Role.AUTHOR))
      {
        result = true;
      }
      break;
    case REVIEWER:// if you are not already a reviewer
      if (!my_users.get(the_user).contains(Role.REVIEWER))
      {
        result = true;
      }
      break;
    case SUB_CHAIR: // Business Rule #5
      if (my_users.get(the_user).contains(Role.REVIEWER)
          && !my_users.get(the_user).contains(Role.SUB_CHAIR)
          && !containsSubchair(the_user))
      {
        result = true;
      }
      break;
    default:
      // should not happen
      throw new IllegalArgumentException("No Such Role");
    }
    return result;
  }

  /**
   * Checks limit for how many papers a user/author has submitted for this
   * conference.
   * 
   * @param the_user
   *          The user/author to check.
   * @return Returns true if the user has submitted 4 papers
   * */
  // @ requires containsUser(the_user);
  // @ requires hasSubmittedAPaper(the_user) == true;
  // @ requires getRoles(the_user).contains(Role.AUTHOR);
  // @ ensures \result == under_limit
  private boolean hasReachedLimit(final User the_user)
  {
    return getPapersByAuthor(the_user).size() >= LIMIT;
  }

  /**
   * Checks whether a specific user has submitted a paper. This method will
   * throw an exception if the user is not registered to the conference.
   * 
   * @param the_user
   *          The user to check.
   * @return Returns true if the user has submitted a paper, false otherwise.
   * */
  // @ requires containsUser(the_user);
  // @ ensures \result == is_an_author;
  public boolean hasSubmittedAPaper(final User the_user)
  {
    if (!containsUser(the_user))
    {
      throw new IllegalArgumentException(the_user
          + " is not added to the conference");
    }
    boolean is_an_author = false;
    for (final Paper p : my_submitted_papers)
    {
      if (p.getAuthor().equals(the_user))
      {
        is_an_author = true;
        break;
      }
    }
    return is_an_author;
  }

  /**
   * Tells you whether this conference has the particular user.
   * 
   * @param the_user
   *          The user to check
   * @return A boolean
   * */
  // @ ensures \result == contains_user;
  public boolean containsUser(final User the_user)
  {
    boolean contains_user = false;
    Iterator<User> itr;
    for (itr = my_users.keySet().iterator(); itr.hasNext();)
    {
      User key = itr.next();
      if (key.equals(the_user))
      {
        contains_user = true;
        break;
      }
    }
    return contains_user;
  }

  /**
   * Tells you whether this conference has a subprogram chair with a specified
   * user.
   * 
   * @param the_user
   *          The user to check
   * @return A boolean
   * */
  // @ ensures \result == contains_subchair;
  public boolean containsSubchair(final User the_user)
  {
    boolean contains_subchair = false;
    for (final User u : my_sub_chairs)
    {
      if (u.equals(the_user))
      {
        contains_subchair = true;
        break;
      }
    }
    return contains_subchair;
  }

  // get methods

  /**
   * Returns a list of all users registered to this conference. This method
   * returns a deep copy of List of User objects.
   * 
   * @return The list of all users to this conference.
   * */
  // @ requires my_users != null;
  // @ ensures \result == my_users.keySet();
  public Set<User> users()
  {
    return my_users.keySet();
  }

  /**
   * Returns a list of roles a user is assigned to this conference.
   * 
   * @param the_user
   *          The user to be checked.
   * @return The list of roles this user is assigned to this conference.
   * */
  // @ requires my_users != null;
  // @ ensures \result == a_list_of_roles;
  public List<Role> getRoles(final User the_user)
  {
    List<Role> list_of_roles = new ArrayList<Role>(my_users.get(the_user));
    
    Collections.copy(list_of_roles, my_users.get(the_user));
    
    return list_of_roles; // a list of roles
  }

  /**
   * Returns a shallow copy of the mappings between users and their current
   * roles to this conference.
   * 
   * @return The mappings between users and their current roles to this
   *         conference.
   * */
  // @ requires my_users != null;
  // @ ensures \result == my_users;
  public Map<User, List<Role>> userMappings()
  {
    return my_users; //TODO must return a deep copy
  }

  /**
   * Returns a User object representation of the program chair.
   * 
   * @return The program chair for the conference.
   * */
  // @ requires programChair() != null;
  // @ ensures \result == the_program_chair;
  public User programChair()
  {
    return my_program_chair.copy();
  }

  /**
   * Returns a list of all subprogram chairs to this conference.
   * 
   * @return Returns a deep copy of a list of subprogram chairs.
   * */
  // @ ensures \result == list_of_sub_chairs;
  public List<User> subProgramChairs()
  {
    List<User> copy = new ArrayList<User>(my_sub_chairs);
    Collections.copy(copy, my_sub_chairs);
    return copy; // returns a deep copy
  }

  /**
   * Returns a string representation of the conference name.
   * 
   * @return A String representation of the name for this conference.
   * */
  public String conferenceName()
  {
    return my_conference_name;
  }

  /**
   * Returns a list of papers accessible by an user/author. If the user has not
   * submitted any papers, this will return an empty list. It does not check
   * whether the user is already an author. Throws an exception if the User is
   * not registered to this conference.
   * 
   * @param the_author
   *          The author.
   * @return A list of papers (deep copy)
   * */
  // @ requires containsUser(the_author);
  // @ ensures \result == list_of_papers_submitted;
  public List<Paper> getPapersByAuthor(final User the_author)
  {
    // Enforces Business Rule #13
    if (!containsUser(the_author))
    {
      throw new IllegalArgumentException(the_author
          + " is not added to the conference");
    }

    List<Paper> papers_by_author = new ArrayList<Paper>();
    for (final Paper p : my_submitted_papers)
    {
      if (p.getAuthor().equals(the_author))
      {
        papers_by_author.add(p);
      }
    }
    return papers_by_author; // a deep copy
  }

  /**
   * Returns a list of papers assigned to a sub program chair. Throws an
   * exception if the User is not registered to this conference.
   * 
   * @param the_sub_chair
   *          The sub program chair.
   * @return A List of papers (deep copy).
   * */
  // @ requires containsUser(the_sub_chair);
  // @ ensures \result == papers_assigned;
  public List<Paper> getPapersAssignedToSubChairs(final User the_sub_chair)
  {
    if (!containsUser(the_sub_chair))
    {
      throw new IllegalArgumentException(the_sub_chair
          + " is not added to the conference");
    }
    List<Paper> papers_for_spc = new ArrayList<Paper>();
    for (final Paper p : my_submitted_papers)
    {
      if ((p.isAssigned() && p.getSubprogramChair().equals(the_sub_chair)))
      {
        papers_for_spc.add(p);
      }
    }
    return papers_for_spc; // returns a deep copy
  }

  /**
   * Returns a list of papers assigned to a reviewer. Throws an exception if the
   * User is not registered to this conference.
   * 
   * @param the_reviewer
   *          The reviewer.
   * @return A list of papers (deep copy).
   * */
  // @ requires containsUser(the_reviewer);
  // @ ensures \result == papers_assigned;
  public List<Paper> getPapersAssignedToReviewers(final User the_reviewer)
  {
    // Enforces Business Rule #11
    if (!containsUser(the_reviewer))
    {
      throw new IllegalArgumentException(the_reviewer
          + " is not added to the conference");
    }

    List<Paper> papersAccessedByTheReviewer = new ArrayList<Paper>();
    for (final Paper p : my_submitted_papers)
    {
      if (p.getMyReviewAssociation().containsKey(the_reviewer))
      {
        papersAccessedByTheReviewer.add(p);
      }
    }
    return papersAccessedByTheReviewer; // returns a deep copy
  }

  /**
   * Returns a list of all submitted papers in this conference. Returns a list
   * of papers accessible by the program chair.
   * 
   * @return A list of papers (deep copy).
   * */
  public List<Paper> getSubmittedPapers()
  {
    List<Paper> copy = new ArrayList<Paper>(my_submitted_papers);
    Collections.copy(copy, my_submitted_papers);
    return copy; // returns a deep copy
  }

  public Date getDeadline()
  {
    return my_submission_deadline;
  }

  @Override
  public int hashCode()
  {
    final int prime = 31;
    int result = 1;
    result = prime * result
        + ((my_conference_name == null) ? 0 : my_conference_name.hashCode());
    result = prime
        * result
        + ((my_submission_deadline == null) ? 0 : my_submission_deadline
            .hashCode());
    return result;
  }

  public void setDeadline(final Date the_deadline)
  {
    my_submission_deadline = the_deadline;
  }

  /**
   * Used for the roles of every user in conference
   * */
  public enum Role
  {
    AUTHOR, PROGRAM_CHAIR, SUB_CHAIR, REVIEWER, NEW_USER;
  }
}
